package components;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;

import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Caret;

import util.FileUtil;

public class PanelReport extends JPanel {
	private static final long serialVersionUID = 1L;

	private String reportsPath = "reports/templates/templates.properties";

	private JTextArea textArea = new JTextArea(10, 50);
	private JScrollPane scrollPane = new JScrollPane(textArea);
	private JPanel panelEast = new JPanel(new JDVerticalFlowLayout());
	
	private TemplateListModel templateListModel = null;
	
	private JList listTemplates = new JList();
	private JScrollPane scrollPaneTemplates = new JScrollPane(listTemplates);
	
	private JPanel panelTemplates = new JPanel(new BorderLayout());

	private JPanel panelButtonsTemplates = new JPanel(new GridLayout(1, 2, 2, 2));

	private JButton btnSave = new JButton("Save");

	private JButton btnAddTemplate = new JButton("Add");
	private JButton btnRemoveTemplate = new JButton("Remove");

	private Properties templates;

	private TemplatesPopUpMenu templatesPopUpMenu;

	private File reportFile;

	public PanelReport(File dir) {

		dir.mkdirs();

		reportFile = new File(dir, "report.txt");

		templates = loadReportTemplates();

		setLayout(new BorderLayout());
		add(scrollPane);
		add(panelEast, BorderLayout.EAST);

		panelEast.add(btnSave);

		btnSave.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				saveReportFile();
			}
		});

		textArea.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_SPACE) {

					Point point = getCaretPosition();

					templatesPopUpMenu = new TemplatesPopUpMenu(point, templates);

				}
				if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
					if (templatesPopUpMenu != null && templatesPopUpMenu.isVisible()) {
						templatesPopUpMenu.hidePopup();
					}
				}
			}
		});

		textArea.addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				if (templatesPopUpMenu != null && templatesPopUpMenu.isVisible()) {
					if (!templatesPopUpMenu.isFocusableWindow()) {
						templatesPopUpMenu.hidePopup();
					}
				}
			}
		});

		listTemplates.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		listTemplates.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					appentTextToCursorPosition(listTemplates.getSelectedValue().toString());
				}
			}
		});

		listTemplates.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				btnRemoveTemplate.setEnabled(listTemplates.getSelectedIndex() != -1);
			}
		});

		btnRemoveTemplate.setEnabled(false);

		panelButtonsTemplates.add(btnRemoveTemplate);
		panelButtonsTemplates.add(btnAddTemplate);

		btnRemoveTemplate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				removeTemplate();
			}
		});

		btnAddTemplate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				addTemplate();
			}
		});

		panelTemplates.add(panelButtonsTemplates, BorderLayout.NORTH);
		panelTemplates.add(scrollPaneTemplates);

		add(panelTemplates, BorderLayout.WEST);

		templateListModel = new TemplateListModel(templates);
		
		listTemplates.setModel(templateListModel);
		
		openReportFile();
	}

	protected void addTemplate() {
		String template = JOptionPane.showInputDialog(this, "Value: ", "Add Template", JOptionPane.PLAIN_MESSAGE);
		if (template != null && !template.isEmpty()) {
			String key = findNextAvaliableKey();

			templates.setProperty(key, template);
			storeReportTemplates(templates);
		}
		
		templateListModel.update(templates);
		
	}

	private String findNextAvaliableKey() {
		int count = 0;

		String keyName = "template" + count;

		while (templates.contains(keyName)) {
			count++;
			keyName = "template" + count;
		}

		return keyName;
	}

	protected void removeTemplate() {

	}

	private void openReportFile() {
		try {

			String content = FileUtil.readFile(reportFile);

			textArea.setText(content);

		} catch (IOException e) {
			reportException(e);
		}

	}

	private void saveReportFile() {
		try {

			FileUtil.writeFile(reportFile, textArea.getText());

			JOptionPane.showMessageDialog(this, "Save successfully!", "Message", JOptionPane.INFORMATION_MESSAGE);

		} catch (IOException e) {
			reportException(e);
		}

	}

	private void reportException(Exception e) {
		JDErrorDialog.reportException(e, this);
	}

	private Point getCaretPosition() {
		Caret caret = textArea.getCaret();
		Point pointCarret = caret.getMagicCaretPosition();
		
		if (pointCarret == null) {
			pointCarret = new Point();
		}
		
		Point p = new Point(pointCarret);
		
		int rowHeight = textArea.getFontMetrics(textArea.getFont()).getHeight();

		p.x += textArea.getLocationOnScreen().x;
		p.y += textArea.getLocationOnScreen().y + rowHeight;

		return p;
	}

	private Properties loadReportTemplates() {

		Properties properties = new Properties();

		FileReader fileReader = null;
		try {

			File file = new File(reportsPath);
			if (file.exists()) {
				fileReader = new FileReader(reportsPath);
				properties.load(fileReader);
			}

		} catch (IOException e) {
			reportException(e);
		} finally {
			FileUtil.close(fileReader);
		}

		return properties;
	}

	private void storeReportTemplates(Properties properties) {
		FileWriter fileWriter = null;
		try {
			if (properties != null) {

				fileWriter = new FileWriter(reportsPath);
				properties.store(fileWriter, new Date().toString());

			}
		} catch (Exception e) {
			reportException(e);
		} finally {
			FileUtil.close(fileWriter);
		}

	}

	private class TemplateListModel extends AbstractListModel {
		private static final long serialVersionUID = 1L;

		private List<Entry<Object, Object>> filteredList = new ArrayList<Entry<Object,Object>>();

		private Properties properties;
		
		public TemplateListModel(Properties properties) {
			update(properties);
		}

		public void update() {
			fireContentsChanged(this, 0, filteredList.size() - 1);	    
    }

		public void update(Properties properties) {
			this.properties = properties;
			filteredList.clear();
			filteredList.addAll(properties.entrySet());
			update();
    }
		
		@Override
		public int getSize() {
			return filteredList != null ? filteredList.size() : 0;
		}

		@Override
		public Object getElementAt(int index) {
			return filteredList != null ? filteredList.get(index).getValue() : null;
		}

		public void filter(String word) {

			if (word != null && !word.isEmpty()) {
				filteredList.clear();

				word = word.toUpperCase();

				for (Entry<Object, Object> entry: templates.entrySet()) {
					String strUpper = ((String)entry.getValue()).toUpperCase();
					
					if (strUpper.startsWith(word)) {
						filteredList.add(entry);
					}
				}
			} else {
				update(properties);
			}

			update();
		}
	}

	public void appentTextToCursorPosition(String text) {
		appentTextToCursorPosition(text, true);
	}

	public void appentTextToCursorPosition(String text, boolean withSpaceSuffix) {
		if (text != null) {
			int cursorIndex = textArea.getCaretPosition();
			if (withSpaceSuffix) {
				if (!text.endsWith(" ")) {
					text = text + " ";
				}
			}

			textArea.insert(text, cursorIndex);
		}
	}

	private class TemplatesPopUpMenu extends JDialog {
		private static final long serialVersionUID = 1L;
		private TemplateListModel templateListModelPopUp;
		private JTextField textField = new JTextField();
		private JList list = new JList();
		private JScrollPane scrollPane = new JScrollPane(list);

		public TemplatesPopUpMenu(Point point, Properties properties) {
			setLayout(new BorderLayout());
			add(scrollPane, BorderLayout.CENTER);
			add(textField, BorderLayout.NORTH);
			templateListModelPopUp = new TemplateListModel(properties);
			list.setModel(templateListModelPopUp);
			setModal(false);

			list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

			if (Toolkit.getDefaultToolkit().isAlwaysOnTopSupported()) {
				setAlwaysOnTop(true);
			}

			setUndecorated(true);

			textField.addKeyListener(new KeyAdapter() {
				@Override
				public void keyReleased(KeyEvent e) {
					templateListModelPopUp.filter(textField.getText());
				}

				@Override
				public void keyPressed(KeyEvent e) {
					if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
						hidePopup();
					} else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_UP) {
						list.dispatchEvent(e);
					}
				}
			});
			textField.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					fireTemplateSelected();
				}
			});

			list.addKeyListener(new KeyAdapter() {
				@Override
				public void keyPressed(KeyEvent e) {
					if (e.getKeyCode() == KeyEvent.VK_ENTER) {
						fireTemplateSelected();
					}
				}
			});

			list.addMouseListener(new MouseAdapter() {
				@Override
				public void mouseClicked(MouseEvent e) {
					if (e.getClickCount() == 2) {
						fireTemplateSelected();
					}
				}
			});

			setLocation(point);
			pack();
			setVisible(true);

		}

		private void fireTemplateSelected() {
			String selected = (String) list.getSelectedValue();

			if (selected == null) {
				if (templateListModelPopUp.getSize() == 1) {
					selected = (String) templateListModelPopUp.getElementAt(0);
				}
			}

			appentTextToCursorPosition(selected);

			hidePopup();
		}

		public void hidePopup() {
			dispose();
		}
	}

	private static void createAndShowGUI() {
		// Create and set up the window.
		JFrame frame = new JFrame("ProgressBarDemo");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Create and set up the content pane.
		JComponent newContentPane = new PanelReport(new File("C:/Temp"));
		newContentPane.setOpaque(true); // content panes must be opaque
		frame.setContentPane(newContentPane);

		// Display the window.
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		// Schedule a job for the event-dispatching thread:
		// creating and showing this application's GUI.
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			@Override
      public void run() {
				createAndShowGUI();
			}
		});
	}

}
